package pl.enigmatic.time;

/**
 * This enum contains constants to control the timing of this thread.
 * 
 * @author Marvin Froehlich (aka Qudus)
 */
public enum TimingMode {
	MILLISECONDS(1000.0, 1L, 1000f, 1000000L), MICROSECONDS(1000000.0, 1000L, 1f, 1000L), NANOSECONDS(1000000000.0, 1000000L, 1f / 1000f, 1L);

	private final double divisor;
	private final long milliDivisor;
	private final long nanoFactor;

	private final float floatDivisor;
	private final float floatMilliDivisor;
	private final float floatMicroFactor;
	private final float floatNanoFactor;

	/**
	 * @return the divisor to divide by to get full seconds.
	 */
	public final double getDivisor() {
		return divisor;
	}

	/**
	 * @return the divisor to divide nanos by to get millis.
	 */
	public final long getMilliDivisor() {
		return milliDivisor;
	}

	/**
	 * @return the factor to multiply <code>this</code> by to get nanos.
	 */
	public final long getNanoFactor() {
		return nanoFactor;
	}

	/**
	 * @return the divisor to divide by to get full seconds.
	 */
	public final float getFloatDivisor() {
		return floatDivisor;
	}

	/**
	 * @return the divisor to divide nanos by to get millis.
	 */
	public final float getFloatMilliDivisor() {
		return floatMilliDivisor;
	}

	/**
	 * @return the factor to multiply <code>this</code> by to get micros.
	 */
	public final float getFloatMicroFactor() {
		return floatMicroFactor;
	}

	/**
	 * @return the factor to multiply <code>this</code> by to get nanos.
	 */
	public final float getFloatNanoFactor() {
		return floatNanoFactor;
	}

	/**
	 * @param t the input time measured in millis, micros or nanos
	 * 
	 * @return the corresponding millis
	 */
	public final long getMilliSeconds(final long t) {
		/*
		 * switch ( this ) { case MILLISECONDS: return ( t ); case MICROSECONDS: return ( t / 1000L ); case NANOSECONDS: return ( t / 1000000L );
		 * default: throw new Error( "This conversion is not yet implemented." ); }
		 */

		return t / milliDivisor;
	}

	/**
	 * @param t the input time measured in millis, micros or nanos
	 * 
	 * @return the corresponding micros
	 */
	public final long getMicroSeconds(final long t) {
		/*
		 * switch ( this ) { case MILLISECONDS: return ( t * 1000L ); case MICROSECONDS: return ( t ); case NANOSECONDS: return ( t / 1000L );
		 * default: throw new Error( "This conversion is not yet implemented." ); }
		 */

		return (long) (t * floatMicroFactor);
	}

	/**
	 * @param t the input time measured in millis, micros or nanos
	 * 
	 * @return the corresponding nanos
	 */
	public final long getNanoSeconds(final long t) {
		/*
		 * switch ( this ) { case MILLISECONDS: return ( t * 1000000L ); case MICROSECONDS: return ( t * 1000L ); case NANOSECONDS: return ( t );
		 * default: throw new Error( "This conversion is not yet implemented." ); }
		 */

		return t * nanoFactor;
	}

	/**
	 * @param t the input time measured in millis, micros or nanos
	 * 
	 * @return the corresponding seconds as float
	 */
	public final float getSecondsAsFloat(final long t) {
		return t / getFloatDivisor();
	}

	/**
	 * @param t the input time measured in millis
	 * 
	 * @return the corresponding time units measured in the current TimingMode
	 */
	public final long getFromMilliSeconds(final long t) {
		/*
		 * switch ( this ) { case MILLISECONDS: return ( t ); case MICROSECONDS: return ( t * 1000L ); case NANOSECONDS: return ( t * 1000000L );
		 * default: throw new Error( "This conversion is not yet implemented." ); }
		 */

		return t * milliDivisor;
	}

	/**
	 * @param t the input time measured in micros
	 * 
	 * @return the corresponding time units measured in the current TimingMode
	 */
	public final long getFromMicroSeconds(final long t) {
		/*
		 * switch ( this ) { case MILLISECONDS: return ( t / 1000L ); case MICROSECONDS: return ( t ); case NANOSECONDS: return ( t * 1000L );
		 * default: throw new Error( "This conversion is not yet implemented." ); }
		 */

		return (long) (t / floatMicroFactor);
	}

	/**
	 * @param t the input time measured in nanos
	 * 
	 * @return the corresponding time units measured in the current TimingMode
	 */
	public final long getFromNanoSeconds(final long t) {
		/*
		 * switch ( this ) { case MILLISECONDS: return ( t / 1000000L ); case MICROSECONDS: return ( t / 1000L ); case NANOSECONDS: return ( t );
		 * default: throw new Error( "This conversion is not yet implemented." ); }
		 */

		return t / nanoFactor;
	}

	private TimingMode(final double divisor, final long milliDivisor, final float microFactor, final long nanoFactor) {
		this.divisor = divisor;
		this.milliDivisor = milliDivisor;
		this.nanoFactor = nanoFactor;

		floatDivisor = (float) divisor;
		floatMilliDivisor = milliDivisor;
		floatMicroFactor = microFactor;
		floatNanoFactor = nanoFactor;
	}
}